package web

import (
	"encoding/json"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	uuid "github.com/satori/go.uuid"
	"log"
	"net/http"
	"time"
)

// GetReplyMsg get JSON format ReplyMessage
func GetReplyMsg(code int, cbkStr string, msg string, status string, data ...interface{}) []byte {
	Dt := make([]interface{}, 5)
	for i := 0; i < 5 && i < len(data); i++ {
		Dt[i] = data[i]
	}
	replyMessage, _ := json.Marshal(&replyMsg{Code: code, Msg: msg, Cbk: cbkStr, Status: status, Data1: Dt[0], Data2: Dt[1], Data3: Dt[2], Data4: Dt[3], Data5: Dt[4]})
	//log.Println("GetReplyMsg", code, cbkStr, msg, status)
	return replyMessage
}

// GetReplyMsgCode get JSON format ReplyMessage lite
func GetReplyMsgCode(code int, cbkStr string, msg string) []byte {
	return GetReplyMsg(code, cbkStr, msg, "")
}

// parseSendMessage 消息解析与转发
func (manager *WsClientManager) parseSendMessage(message []byte) {
	getMsg := &WsMessage{}
	err := json.Unmarshal(message, getMsg)
	if err != nil {
		log.Println("解析错误：", err)
		return
	}

	//发送者为空
	if getMsg.Sender == "" {
		log.Println("无发送者：", err)
		return
	}

	_, ok := manager.Clients.Load(getMsg.Sender)
	if !ok {
		log.Println("发送者不在客户端列表：", getMsg.Sender)
		return
	}

	getMsg.MsgId = uuid.NewV4().String()
	getMsg.SysTime = time.Now().Unix()
	getMsg.SysTimeNano = time.Now().UnixNano()
	getMsg.Code = 198 //表示此为消息
	//1V1
	if getMsg.Recipient != "" {
		if getMsg.Recipient == "system" {
			manager.execSystemCommand(getMsg, message)
		} else {
			// 有接收者，则进行1v1消息发送
			manager.p2p(getMsg)
		}
	} else if (getMsg.Appid + "/" + getMsg.Roomid) != "" {
		// 有appid和roomid，则进行广播
		manager.send2room(getMsg)
	} else {
		Try(func() {
			v, ok := manager.Clients.Load(getMsg.Sender)
			if ok {
				v.(*WsClient).Send <- GetReplyMsgCode(403, getMsg.Cbk, "Empty Recipient")
			}
		}).Finally(func() {

		})

	}
}

func (manager *WsClientManager) send2room(getMsg *WsMessage) {
	roomid := getMsg.Appid + "/" + getMsg.Roomid
	_, ok := manager.Rooms.Load(roomid)
	if ok {
		manager.eachRoom(roomid, func(client *WsClient) {
			d, _ := json.Marshal(getMsg)
			Try(func() {
				client.Send <- d
			}).Finally(func() {

			})
		})
	}
}

// 遍历房间中的每个人
func (manager *WsClientManager) eachRoom(roomid string, eachCbk func(client *WsClient)) {
	wsRoom, ok := manager.Rooms.Load(roomid)
	if ok {
		wsRoom.(*WsRoom).Clients.Range(func(k, value interface{}) bool {
			if cc, ok := manager.Clients.Load(k); ok {
				if cc != nil {
					if eachCbk != nil {
						eachCbk(cc.(*WsClient))
					}
				} else {
					log.Println("删除客户端", k)
					//删除room中断开的客户端
					wsRoom.(*WsRoom).Clients.Delete(k)
					manager.Clients.Delete(k)
				}
			} else {
				log.Println("删除客户端", k)
				//删除room中断开的客户端
				wsRoom.(*WsRoom).Clients.Delete(k)
			}
			return true
		})
	}
}

func (manager *WsClientManager) p2p(getMsg *WsMessage) {
	Recipient, ok := manager.Clients.Load(getMsg.Recipient)
	if ok {
		if Recipient != nil {
			d, _ := json.Marshal(getMsg)

			Try(func() {
				Recipient.(*WsClient).Send <- d
			}).Catch(func(e interface{}) {
				log.Println(e)
			}).Finally(func() {

			})
		} else {
			log.Println(getMsg.Recipient, "Recipient已断开")
			manager.Clients.Delete(getMsg.Recipient)
			replyMessage, _ := json.Marshal(&replyMsg{Code: 405, Msg: "Found Recipient But Offline"})
			Sender, ok := manager.Clients.Load(getMsg.Sender)
			if ok {
				if Sender != nil {
					Try(func() {
						Sender.(*WsClient).Send <- replyMessage
					}).Finally(func() {

					})
				} else {
					log.Println(getMsg.Sender, "Sender不存在")
					manager.Clients.Delete(getMsg.Sender)
				}
			}
		}
	} else {
		//log.Println(getMsg.Recipient, "Recipient不存在")
		replyMessage, _ := json.Marshal(&replyMsg{Code: 404, Msg: "Not Found Recipient"})
		if Sender, ok := manager.Clients.Load(getMsg.Sender); ok {
			if Sender != nil {
				Try(func() {
					Sender.(*WsClient).Send <- replyMessage
				}).Finally(func() {

				})
			} else {
				log.Println(getMsg.Sender, "Sender不存在")
				manager.Clients.Delete(getMsg.Sender)
			}
		}
	}
}

func (manager *WsClientManager) execSystemCommand(getMsg *WsMessage, message []byte) bool {
	if getMsg.Content == "" {
		Try(func() {
			Sender, ok := manager.Clients.Load(getMsg.Sender)
			if ok {
				Sender.(*WsClient).Send <- GetReplyMsgCode(406, getMsg.Cbk, "System Message Parse Error")
			}

		}).Finally(func() {

		})

		return true
	}
	//系统消息
	getSysMsg := &WsSystemMessage{}
	err := json.Unmarshal(message, getSysMsg)
	if err != nil {
		log.Println("转换失败")
		Sender, ok := manager.Clients.Load(getMsg.Sender)
		if ok && Sender != nil {

			Try(func() {
				Sender.(*WsClient).Send <- GetReplyMsgCode(407, getMsg.Cbk, "System Message Data Parse Error")
			}).Finally(func() {

			})
		}
		return true
	}
	var replyMessage = GetReplyMsgCode(408, getMsg.Cbk, "unsupported command")
	//消息合法性判断
	if getMsg.Sender != "" && getSysMsg.Content.Ctype != "" && getSysMsg.Content.Appid != "" && getSysMsg.Content.Roomid != "" {
		roomid := getSysMsg.Content.Appid + "/" + getSysMsg.Content.Roomid
		log.Println("roomid", roomid)
		switch getSysMsg.Content.Ctype {
		case "room.in":
			room, ok := manager.Rooms.Load(roomid)
			if !ok {
				room := WsRoom{}
				manager.Rooms.Store(roomid, &room)
			}
			room, ok = manager.Rooms.Load(roomid)
			room.(*WsRoom).Clients.Store(getMsg.Sender, getSysMsg.Content.Param1)
			manager.eachRoom(roomid, nil)
			replyMessage = GetReplyMsg(200, getMsg.Cbk, "join ok", "", room.(*WsRoom).Clients)
		case "room.out":
			room, ok := manager.Rooms.Load(roomid)
			if ok {
				room.(*WsRoom).Clients.Delete(getMsg.Sender)
			}
			replyMessage = GetReplyMsgCode(200, getMsg.Cbk, "room out ok")

		case "room.members":
			room, ok := manager.Rooms.Load(roomid)
			manager.eachRoom(roomid, nil)
			if ok {
				replyMessage = GetReplyMsg(200, getMsg.Cbk, "", "", room.(*WsRoom).Clients)
			} else {
				replyMessage = GetReplyMsgCode(200, getMsg.Cbk, "empty room")
			}
		}
		Try(func() {
			cc, ok := manager.Clients.Load(getMsg.Sender)
			if ok {
				cc.(*WsClient).Send <- replyMessage
			}
		}).Finally(func() {

		})

	} else {
		Try(func() {
			cc, ok := manager.Clients.Load(getMsg.Sender)
			if ok {
				cc.(*WsClient).Send <- GetReplyMsgCode(409, getMsg.Cbk, "params not compatible")
			}
		}).Finally(func() {

		})
	}
	return true
}

// Start is to start a ws server
func (manager *WsClientManager) Start() {
	for {
		select {
		case conn := <-manager.Register:
			manager.Clients.Store(conn.ID, conn)
			Try(func() {
				conn.Send <- GetReplyMsg(199, "", "", "WELCOME", conn.ID, conn.IDSecret)
			}).Finally(func() {

			})

		case conn := <-manager.Unregister:
			if cc, ok := manager.Clients.Load(conn.ID); ok {
				Try(func() {
					close(conn.Send)
				}).Finally(func() {
					if conn.connID == cc.(*WsClient).connID {
						manager.Clients.Delete(conn.ID)
					}
				})
			}
		case message := <-manager.Broadcast:
			manager.parseSendMessage(message)
		}
	}
}

func (c *WsClient) Read() {
	defer func() {
		WsManager.Unregister <- c
		_ = c.Socket.Close()
	}()

	log.Println("监听数据接收")
	for {
		_, message, err := c.Socket.ReadMessage()
		//log.Println(message)
		if err != nil {
			log.Println(err)
			break
		}
		if string(message) == "ping" || string(message) == "HeartBeat" {
			_ = c.Socket.WriteMessage(websocket.TextMessage, []byte("pong"))
		} else {
			WsManager.Broadcast <- message
		}
	}
}

func (c *WsClient) Write() {
	defer func() {
		WsManager.Unregister <- c
		_ = c.Socket.Close()
	}()
	log.Println("监听数据写出")
	lock := false
	for {
		select {
		case message, ok := <-c.Send:
			//log.Println(string(message))
			if !ok {
				log.Println("断开")
				_ = c.Socket.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			for {
				time.Sleep(5 * time.Millisecond)
				if !lock {
					break
				}
			}
			lock = true
			_ = c.Socket.WriteMessage(websocket.TextMessage, message)
			lock = false
		}
	}
}

// 处理Websocket
func WsPage(c *gin.Context) {
	// change the reqest to websocket model
	conn, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		http.NotFound(c.Writer, c.Request)
		return
	}

	qUid := c.Query("uid")
	qUidsec := c.Query("uidsec")
	qLabel := c.Query("label")

	cUid, _ := c.Cookie("uid")
	cUidsecret, _ := c.Cookie("uidsec")

	uid, uidsecret := "", ""
	if checkUID(qUid, qUidsec) {
		uid = qUid
		uidsecret = qUidsec
	} else if checkUID(cUid, cUidsecret) {
		uid = cUid
		uidsecret = cUidsecret
	} else {
		uid = uuid.NewV4().String()
		uidsecret = GetSHA256HashCode([]byte(uid + solt))
	}
	if cc, ok := WsManager.Clients.Load(uid); ok {
		log.Println("存在旧连接")
		Try(func() {
			log.Println("关闭旧连接")
			close(cc.(*WsClient).Send)
			_ = cc.(*WsClient).Socket.Close()
		}).Finally(func() {
			log.Println("执行1")
		})
	}

	client := &WsClient{ID: uid, label: qLabel, IDSecret: uidsecret, connID: uuid.NewV4().String(), Socket: conn, Send: make(chan []byte)}
	log.Println(client.ID)
	WsManager.Register <- client
	go client.Read()
	go client.Write()

	defer func() {
		if e := recover(); nil != e {
			log.Println(e)
		}
	}()
}
